package com.ybear.ybcomponent.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;

import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.customview.widget.ViewDragHelper;

import com.ybear.ybcomponent.R;
import com.ybear.ybcomponent.Utils;

/**
 * 子项侧滑布局
 */
public class ItemSwipeLayout extends FrameLayout {
    public interface CallViewPositionHorizontalListener {
        void call(@NonNull SwipeDragHelper swipeDrag, @NonNull View child, int distance, int vel);
    }

    public interface OnSwipeItemClickListener {
        void onClick(ItemSwipeLayout view, View childView, int position);
    }

    public interface OnDragStateChangedListener {
        void onChanged(@NonNull ItemSwipeLayout swipeDrag, STATE state);
    }

    public enum STATE {
        STATE_IDLE, STATE_DRAGGING, STATE_SETTLING
    }

    private Paint mPaint;
    private PaintFlagsDrawFilter mDrawFilter;
    private PorterDuffXfermode mXfermode;

    private ViewGroup mSwipeLayout;
    private ViewDragHelper mViewDrag;
    private SwipeDragHelper mSwipeDrag;
    private OnClickListener mOnClickListener;
    private OnSwipeItemClickListener mOnSwipeItemClickListener;
    private OnDragStateChangedListener mOnDragStateChangedListener;

    private int radius;
    //侧滑子项的最小宽度
    private int swipeItemMinWidth;
    private int dragVelX;
    private int swipeGravity;
//    private boolean isSwipeStateOn = false;
    private boolean enableMultiSwipeDrag, enableSwipeDrag;

    public ItemSwipeLayout(Context context) {
        this(context, null);
    }

    public ItemSwipeLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ItemSwipeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    /**
     * 初始化传入的参数
     */
    private void initTypeArray(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(
                attrs, R.styleable.ItemSwipeLayout
        );
        int defItemMinWidth = Utils.dp2Px(getContext(), 50);

        //拖动X的灵敏系数
        dragVelX = typedArray.getInteger(
                R.styleable.ItemSwipeLayout_itemSwipeDragVelX, 20
        );
        //swipe控件显示的位置
        swipeGravity = typedArray.getInteger(
                R.styleable.ItemSwipeLayout_itemSwipeGravity, Gravity.END
        );
        //侧滑控件最小宽度
        swipeItemMinWidth = typedArray.getDimensionPixelSize(
                R.styleable.ItemSwipeLayout_itemSwipeItemMinWidth, defItemMinWidth
        );
        //允许多点拖动。false时只响应首次拖动的pointerId
        enableMultiSwipeDrag = typedArray.getBoolean(
                R.styleable.ItemSwipeLayout_itemSwipeEnableMultiSwipeDrag, true
        );
        //是否启用拖动。默认启用
        enableSwipeDrag = typedArray.getBoolean(
                R.styleable.ItemSwipeLayout_itemSwipeEnableSwipeDrag, true
        );
        //圆角
        radius = typedArray.getDimensionPixelSize(
                R.styleable.ItemSwipeLayout_itemSwipeRadius, -1
        );

        typedArray.recycle();
    }

    /**
     * 初始化
     */
    private void init(Context context, AttributeSet attrs) {
        initTypeArray( context, attrs );

        mPaint = new Paint();
        //画布抗锯齿
        mDrawFilter = new PaintFlagsDrawFilter(
                0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG
        );

        //启用硬件加速，否则会出现一些异常（比如黑边，设计器和模拟器可能依旧会存在黑边）
        setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
        /* 保留上层的圆角，圆形 */
        mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP);

        mSwipeDrag = new SwipeDragHelper( this );
        //创建swipe布局
        doSwipeLayout( swipeGravity );

        //允许多点拖动
        setEnableMultiSwipeDrag( enableMultiSwipeDrag );
        setEnableSwipeDrag( enableSwipeDrag );
        //控件水平拖拽事件监听器
        mSwipeDrag.setOnViewPositionHorizontalListener((swipeDrag, child, distance, vel) -> {
            if( !enableSwipeDrag ) return;
            int swipeWidth = mSwipeLayout.getWidth();
            //滑动距离为正数
            int distanceAbs = Math.abs( distance );
            //滑动方向是否在左边
            boolean isStart = swipeGravity == Gravity.START;
            //允许向左或者向右滑动（向左distance为负数，向右distance为正数）
            boolean isLR = isStart ? distance >= 0 : distance <= 0;
            //是否处于拖动范围（超出灵敏系数值 或 滑动超出swipe控件一半宽度）
            boolean isDragScope = Math.abs( vel ) >= dragVelX || distanceAbs >= swipeWidth / 2;

            //允许拖拽
            if( isLR && isDragScope ) {
                //当前swipe控件为不可见状态（关闭状态）
                swipeDrag.setCapturedLeft( isStart ? swipeWidth : -swipeWidth );
            }

            //只允许在swipe控件范围内滑动
            if( distanceAbs >= swipeWidth ) {
                swipeDrag.setEnableHorizontal( false );
            }else {
                //允许滑动的方向（左/右）
                swipeDrag.setEnableHorizontal( isStart ? distance > 0 : distance < 0 );
            }
        });
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if( mViewDrag == null ) {
            mViewDrag = ViewDragHelper.create( this, 1.0F, mSwipeDrag );
        }
        View v = getItemView();
        if( v == null ) return;
        ViewGroup.LayoutParams lpItem = v.getLayoutParams();
        int size = ViewGroup.LayoutParams.MATCH_PARENT;
        if( lpItem == null ) {
            lpItem = new LayoutParams( size, size );
        }else {
            lpItem.width = size;
            lpItem.height = size;
        }
        v.setLayoutParams( lpItem );
        //设置允许拖动的控件
        if( mSwipeDrag == null ) return;
        mSwipeDrag.setCaptureViews( v );
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //委托控件拖拽
        return mViewDrag == null ?
                super.onInterceptTouchEvent( ev ) :
                mViewDrag.shouldInterceptTouchEvent( ev );
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        View v = getItemView();
        if( v == null ) return super.onTouchEvent( ev );
        int left = getItemViewLeft();
        //设置委托控件拖拽
        if( mViewDrag != null ) mViewDrag.processTouchEvent( ev );
        /* swipe控件不可见时才能回调item的点击事件以及重置拖拽状态 */
        if( left == 0 ) {
            //回调布局中的控件点击事件监听器
            if( mOnClickListener != null && ev.getAction() == MotionEvent.ACTION_UP ) {
                mOnClickListener.onClick( v );
            }
        }else if( left == mSwipeLayout.getWidth() ) {
            //重置拖拽状态
            resetDrag( ev );
        }
        return true;
    }

    /**
     * 回弹时刷新控件
     */
    @Override
    public void computeScroll() {
        if( mViewDrag == null || !mViewDrag.continueSettling( true ) ) return;
        postInvalidate();
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if( radius != -1 ) {
            //启用抗锯齿
            mPaint.setAntiAlias( true );
            //消除画布的抗锯齿
            canvas.setDrawFilter( mDrawFilter );
            //获取绘制弧形部分的图片
            Bitmap shapeBmp = getDrawShapeBitmap();
            //绘制时消除绘制的圆角 (或者注释掉看效果就明白了)
            mPaint.setXfermode( mXfermode );
            //绘制最终的结果
            canvas.drawBitmap(shapeBmp, 0, 0, mPaint);
        }
    }

    private final RectF mDrawRectF = new RectF();
    private final Canvas mDrawCanvas = new Canvas();
    /**
     * 绘制圆角、圆形部分的图片。
     * 因为用的一个paint，所以需要在设置{@link Paint#setXfermode(Xfermode)} 之前获取圆角、圆形图片
     */
    private Bitmap getDrawShapeBitmap() {
        //建立一个空白图片，在里边绘制圆角、圆形
        Bitmap shapeBmp = Bitmap.createBitmap(
                getWidth(), getHeight(), Bitmap.Config.ARGB_8888
        );
        //建立矩形
        mDrawRectF.set( 0, 0, getWidth(), getHeight() );
        //清空画布
        mDrawCanvas.drawColor( Color.TRANSPARENT );
        //建立画布
        mDrawCanvas.setBitmap( shapeBmp );
        //消除画布的抗锯齿
        mDrawCanvas.setDrawFilter( mDrawFilter );
        //绘制圆角
        mDrawCanvas.drawRoundRect( mDrawRectF, radius, radius, mPaint );
        return shapeBmp;
    }


    /**
     * 设置swipe控件（侧滑出现的子控件）
     * @param minWidth  控件最小宽度
     * @param isOnly    true：无论设置多少次，只有首次调用时有效，false：清空之前的设置并重新设置
     * @param views     swipe控件。允许不设置宽度，默认宽度：{@link #swipeItemMinWidth}
     */
    public void setSwipeViews(int minWidth, boolean isOnly, View... views) {
        if( mSwipeLayout.getChildCount() > 0 ) {
            if( isOnly ) return;
            mSwipeLayout.removeAllViews();
        }
        //处理侧滑控件
        post(() -> {
            for (int i = 0; i < views.length; i++) {
                doSwipeView( views[ i ], minWidth, i );
            }
        });

    }
    public void setSwipeViews(boolean isOnly, View... views) {
        setSwipeViews(swipeItemMinWidth, isOnly, views);
    }
    public void setSwipeViews(View... views) {
        setSwipeViews(true, views);
    }

    /**
     * 重置拖拽控件到初始状态
     */
    public void resetDragView() {
        View v = getItemView();
        if( v == null || mViewDrag == null ) return;
        if( !mViewDrag.smoothSlideViewTo( v, 0, 0 ) ) return;
        //重置到初始状态
        mSwipeDrag.setCapturedLeft( 0 );
        ViewCompat.postInvalidateOnAnimation( this );
        postInvalidate();
    }

    /**
     * 获取布局中的主控件
     * @return  控件
     */
    @Nullable
    public View getItemView() { return getChildAt( 1 ); }

    /**
     * 获取侧滑布局
     * @return  布局
     */
    public ViewGroup getSwipeLayout() {
        return mSwipeLayout;
    }

    /**
     * 设置swipe组件最小宽度
     * @param widthDP   最小宽度
     * @return          this
     */
    public ItemSwipeLayout setSwipeItemMinWidth(int widthDP) {
        swipeItemMinWidth = Utils.dp2Px( getContext(), widthDP );
        return this;
    }

    /**
     * swipe控件的显示位置
     * @param gravity   {@link Gravity#START}、{@link Gravity#END}
     * @return          this
     */
    public ItemSwipeLayout setSwipeGravity(int gravity) {
        swipeGravity = gravity != Gravity.START && gravity != Gravity.END ? Gravity.END : gravity;
        doSwipeLayout( swipeGravity );
        return this;
    }

    /**
     * 拖动X的灵敏系数
     * @param velX  系数值。趋近于0时灵敏越高
     * @return      this
     */
    public ItemSwipeLayout setDragVelX(@IntRange(from = 0) int velX) {
        dragVelX = velX;
        return this;
    }

    /**
     * 是否启用多点拖动
     * @param enable    是否启用
     * @return          this
     */
    public ItemSwipeLayout setEnableMultiSwipeDrag(boolean enable) {
        enableMultiSwipeDrag = enable;
        mSwipeDrag.setEnableMultiSwipeDrag( enable );
        return this;
    }
    public boolean isEnableMultiSwipeDrag() {
        return enableMultiSwipeDrag;
    }

    /**
     * 是否启用侧滑
     * @param enable    是否启用
     * @return          this
     */
    public ItemSwipeLayout setEnableSwipeDrag(boolean enable) {
        enableSwipeDrag = enable;
        mSwipeDrag.setEnableHorizontal( enable );
        return this;
    }
    public boolean isEnableSwipeDrag() {
        return enableSwipeDrag;
    }

    /**
     * 设置布局中的控件点击事件监听器
     * @param l     监听器
     */
    public void setOnClickListener(OnClickListener l) {
        mOnClickListener = l;
    }

    /**
     * 设置侧滑子控件点击事件监听器
     * @param listener  监听器
     */
    public void setOnSwipeItemClickListener(OnSwipeItemClickListener listener) {
        mOnSwipeItemClickListener = listener;
    }

    /**
     * 设置拖动状态发生改变事件监听器
     * @param listener  监听器
     */
    public void setOnDragStateChangedListener(OnDragStateChangedListener listener) {
        mOnDragStateChangedListener = listener;
    }

    /**
     * 重置拖拽状态
     */
    private void resetDrag(MotionEvent ev) {
        //滑动X轴处于左边还是右边
        float x = swipeGravity == Gravity.START ? ev.getX() : getWidth() - ev.getX();
        //是否处于滑动范围
        boolean isResetScope = x >= mSwipeLayout.getWidth();
        //重置范围
        if( isResetScope && ev.getAction() == MotionEvent.ACTION_DOWN ) {
            //重置拖拽控件到初始状态
            resetDragView();
        }
    }

    /**
     * 处理侧滑控件
     * @param v             侧滑控件
     * @param minWidth      控件最小宽度
     * @param position      当前控件下标
     */
    private void doSwipeView(View v, int minWidth, int position) {
        if( v.getLayoutParams() == null ) {
            v.setLayoutParams(new LayoutParams(
                    minWidth, ViewGroup.LayoutParams.MATCH_PARENT)
            );
        }
        //设置子控件下标位置
        v.setTag( R.id.tag_position, position );
        //回调子控件的点击事件
        Utils.setOnSuperTouchListener(v, (v1, event) -> {
            //处于可点击范围
            if( getItemViewLeft() >= mSwipeLayout.getWidth() ) {
                //处于抬起状态时回调点击事件
                if( event.getAction() == MotionEvent.ACTION_UP ) doOnSwipeItemClick( v1 );
                return true;
            }
            return false;
        });
        mSwipeLayout.addView( v );
    }

    /**
     * 处理Swipe控件的点击事件
     * @param v  控件
     */
    private void doOnSwipeItemClick(View v) {
        if( mOnSwipeItemClickListener == null ) return;
        mOnSwipeItemClickListener.onClick(
                this, v, v == null ? -1 : (int) v.getTag( R.id.tag_position )
        );
    }

    /**
     * 处理swipe控件的布局。创建swipe控件以及显示的位置
     * @param gravity   显示的位置。{@link Gravity#START}、{@link Gravity#END}
     */
    private void doSwipeLayout(int gravity) {
        FrameLayout.LayoutParams lp;
        if( mSwipeLayout == null ) addView( mSwipeLayout = createSwipeLayout() );      //NonNull
        lp = (LayoutParams) mSwipeLayout.getLayoutParams();                            //NonNull
        lp.gravity = gravity;
        mSwipeLayout.setLayoutParams( lp );
    }

    /**
     * 创建swipe控件布局
     * @return  布局
     */
    @NonNull
    private LinearLayout createSwipeLayout() {
        LinearLayout layout = new LinearLayout( getContext() );
        layout.setLayoutParams( new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT
        ));
        return layout;
    }

    /**
     * 获取ItemView左边的距离
     * @return      左边的距离
     */
    private int getItemViewLeft() {
        View v = getItemView();
        return v == null ? 0 : Math.abs( v.getLeft() );
    }

    /**
     * 控件拖拽帮助类
     */
    class SwipeDragHelper extends ViewDragHelper.Callback {
        private CallViewPositionHorizontalListener mHorizontalCall;
        private final ItemSwipeLayout mLayout;
        private View[] mCaptureViews;
        private int initCapturedLeft = -1;
        //启用水平滑动
        private boolean enableHorizontal = true;
        //允许多点拖动。false时只响应首次拖动的pointerId
        private boolean enableMultiSwipeDrag = true;

        SwipeDragHelper(ItemSwipeLayout layout, View... captureViews) {
            mLayout = layout;
            mCaptureViews = captureViews;
        }
        void setCaptureViews(View... captureViews) {
            mCaptureViews = captureViews;
        }
        void setCapturedLeft(int left) {
            initCapturedLeft = left;
        }
        void setEnableHorizontal(boolean enable) {
            enableHorizontal = enable;
        }
        void setEnableMultiSwipeDrag(boolean enable) {
            enableMultiSwipeDrag = enable;
        }

        /**
         * 处理是否允许拖动的控件
         * @param child         拖动控件
         * @param pointerId     多点触控。当前控件同时触控的数量
         * @return              是否允许拖动
         */
        @Override
        public boolean tryCaptureView(@NonNull View child, int pointerId) {
            if( mCaptureViews == null ) return true;
            boolean isMulti = enableMultiSwipeDrag || pointerId == 0;
            for( View view : mCaptureViews ) {
                if( view == child && isMulti ) return true;
            }
            return false;
        }

        /**
         * 控件被拖动
         * @param capturedChild     拖动的控件
         * @param activePointerId   拖动的Id
         */
        @Override
        public void onViewCaptured(@NonNull View capturedChild, int activePointerId) {
            super.onViewCaptured(capturedChild, activePointerId);
            if( initCapturedLeft == -1 ) initCapturedLeft = capturedChild.getLeft();
        }

        /**
         * 控件停止拖动
         * @param releasedChild     停止拖动的控件
         * @param xvel              当前X轴速率
         * @param yvel              当前Y轴速率
         */
        @Override
        public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            /* 回弹操作 */
            mViewDrag.settleCapturedViewAt(initCapturedLeft, 0);
            invalidate();
        }

        /**
         * 控件拖动状态发生改变
         * @param state     STATE_IDLE 闲置状态
         *                  STATE_DRAGGING 正在拖动
         *                  STATE_SETTLING 放置到某个位置
         */
        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
            STATE dragState = STATE.STATE_IDLE;
            switch ( state ) {
                case ViewDragHelper.STATE_DRAGGING:
                    dragState = STATE.STATE_DRAGGING;
                    break;
                case ViewDragHelper.STATE_SETTLING:
                    dragState = STATE.STATE_SETTLING;
                    break;
            }
            if( mOnDragStateChangedListener != null ) {
                mOnDragStateChangedListener.onChanged( mLayout, dragState );
            }
        }

        /**
         * 水平拖动控件时回调
         * @param child     拖动的控件
         * @param left      左边的距离
         * @param dx        X轴速率
         * @return          当返回left时允许水平拖动
         */
        @Override
        public int clampViewPositionHorizontal(@NonNull View child, int left, int dx) {
            if( mHorizontalCall != null ) mHorizontalCall.call(this, child, left, dx);
            return enableHorizontal ? left : initCapturedLeft;
        }

        public void setOnViewPositionHorizontalListener(CallViewPositionHorizontalListener call) {
            mHorizontalCall = call;
        }
    }
}
